Skip to content

applet.calibrate_clock: internal&external clock error measurement against external frequency ref#1160

Open
i-infra wants to merge 5 commits intoGlasgowEmbedded:mainfrom
i-infra:ii/calibrate_clock
Open

applet.calibrate_clock: internal&external clock error measurement against external frequency ref#1160
i-infra wants to merge 5 commits intoGlasgowEmbedded:mainfrom
i-infra:ii/calibrate_clock

Conversation

@i-infra
Copy link
Copy Markdown

@i-infra i-infra commented Apr 26, 2026

This PR adds a clock calibration applet supporting calibrating the Glasgow's internal crystal oscillator against an external frequency reference, as well as measuring the frequency error of an external signal against an external frequency reference.

I have tested with two references: GPS PPS (1Hz) input, as well as with a Rubidium frequency standard (0-2v sine wave @ 2**23 Hz), against both the internal crystal oscillator and a Si5351a clockgen chip as external signal.

This PR benefits from #1157 (or workaround firmware mentioned in that issue) - esp to allow the GPS receiver to not lose satt. lock when switching applets, as well as PR #1158 to offer more ergonomic control of a si5351a (for calibrating the Si5351a module's onboard crystal.)

Cheers!

… Rb freq standard or GPS PPS, supporting measuring internal XCO or external input against external reference.
@i-infra i-infra requested a review from whitequark as a code owner April 26, 2026 02:07
Copy link
Copy Markdown
Member

@whitequark whitequark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Please include documentation and tests as discussed elsewhere.

I think the placement of the applet is not correct. "internal" is a catchall category for things that are exclusively used to self-maintain the Glasgow device; it is not expected that an end user would ever want to use an "internal" applet to do an unrelated task.

I do not have a better name, let's just kick this can down the road and decide on it once the other fixes go in.

Comment thread software/glasgow/applet/internal/calibrate_clock/__init__.py Outdated
Comment thread software/glasgow/applet/internal/calibrate_clock/__init__.py Outdated
Comment thread software/glasgow/applet/internal/calibrate_clock/__init__.py Outdated
Comment thread software/glasgow/applet/measure/calibrate_clock/__init__.py
@i-infra
Copy link
Copy Markdown
Author

i-infra commented May 1, 2026

@whitequark I think I have addressed all your comments / suggestions for this applet! We still should figure out where it goes in the tree, and then I'll update this branch accordingly ^^

@whitequark
Copy link
Copy Markdown
Member

We still should figure out where it goes in the tree, and then I'll update this branch accordingly ^^

Let's add a measure taxon. The logic analyzer will also end up there (when properly implemented).

@i-infra
Copy link
Copy Markdown
Author

i-infra commented May 4, 2026

measure sounds great! I have a few other applets that are WIPs / sketches that will end up filed there too, such as..

Pseudorandom Noise Source
================

Generates a pseudo-random binary noise signal from a 31-bit maximal-length
LFSR running at 192 MHz with DDR output, giving an effective chip rate of
384 Mcps (48 MHz system clock x4 via iCE40 PLL, doubled by SB_IO DDR).

The output is a single GPIO pin.  The on-board 33 Ohm series resistor limits
drive current; an external low-pass filter shapes the spectrum for a
band-limited noise source.

Use case: mostly characterising RF filters.  The 384 Mcps effective rate provides
flat spectral coverage to ~192 MHz; odd harmonics extend useful energy to
~960 MHz and beyond (attenuated by the 33 Ohm + parasitic-C channel response).

LFSR polynomial: x^31 + x^28 + 1  (maximal-length, period 2^31-1)
Sequence period at 384 Mcps: ~5.6 seconds before repeat.

Architecture::

    48 MHz system clock
      -> iCE40 SB_PLL40_CORE  (DIVF=15, DIVR=0, DIVQ=2 -> 192 MHz)
        -> 31-bit Galois LFSR, 2 steps per clock cycle
          -> DDR output register (posedge = step 1, negedge = step 2)
            -> io.DDRBuffer -> GPIO pin  (384 Mcps effective)

Host control (fire-and-forget via out-pipe):
    CMD_START (0x01) + seed[3:0] (4 bytes little-endian, non-zero)
    CMD_STOP  (0x02)
    

And some more "weird fake analog stuff" (LC meter with better dynamic range than ~any other open source ones?) (an implementation of the AVR Transistor Tester that uses a small external PCB with a few passives + a SPI ADC / DAC+Comparator, but with much better dynamic range than the clock-limited AVR version?)

@whitequark
Copy link
Copy Markdown
Member

Pseudorandom Noise Source

Oh this looks great!

@whitequark
Copy link
Copy Markdown
Member

whitequark commented May 6, 2026

Looks all good! Please squash the commits (or rebase them into a series of self-contained diffs, up to you in this case) and then you can merge it (I think you should be able to click the button yourself).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants